#ifndef inputmanager_h__
#define inputmanager_h__
#include "gtl/list.h"

namespace gecko
{

	// data for an input event
	struct InputEvent
	{
		// flags
		enum Flags
		{
			Flags_ButtonDown		= 0x0001,
			Flags_ButtonPressed		= 0x0002,
			Flags_ButtonReleased	= 0x0004,
			Flags_AxisMove			= 0x0008,
		};

		// the input ID number (mapped to a game action internally)
		int inputID;

		// 1st input value
		float value1;

		// 2nd input value
		float value2;

		// event specific flags
		int flags;

		// the device that generated the event
		int deviceID;

		// the player the device belongs to
		int playerID;
	};

	// abstract interface for input listeners
	class IInputListener
	{
	public:
		// called when an action is dispatched
		virtual void OnAction(InputEvent & action) = 0;

	private:
		// the player that is associated with the listener
		int m_playerID;

		// allow input manager access to inner-workings
		friend class IInputManager;
	};

	// manager class for reading inputs and dispatching game actions
	class IInputManager
	{
	public:
		// input events (map these to game actions [1:1])
		enum Evt
		{
			Evt_Invalid = -1,

			// pad events
			Evt_PadLThumbMove = 0,
			Evt_PadRThumbMove,
			Evt_PadLThumbUp,
			Evt_PadLThumbDown,
			Evt_PadLThumbLeft,
			Evt_PadLThumbRight,
			Evt_PadRThumbUp,
			Evt_PadRThumbDown,
			Evt_PadRThumbLeft,
			Evt_PadRThumbRight,	
			Evt_PadLeftTrigger,
			Evt_PadRightTrigger,
			Evt_PadA,
			Evt_PadB,
			Evt_PadX,
			Evt_PadY,
			Evt_PadDUp,
			Evt_PadDDown,
			Evt_PadDLeft,
			Evt_PadDRight,
			Evt_PadStart,
			Evt_PadBack,
			Evt_PadLeftThumb,
			Evt_PadRightThumb,
			Evt_PadLShoulder,
			Evt_PadRShoulder,

			// mouse events
			Evt_MouseLeftButton,
			Evt_MouseMiddleButton,
			Evt_MouseRightButton,
			Evt_MouseWheelUp,
			Evt_MouseWheelDown,
			Evt_MouseMove,

			// keyboard events
			Evt_UpArrow,
			Evt_DownArrow,
			Evt_LeftArrow,
			Evt_RightArrow,
			Evt_Q,
			Evt_A,
		
			Evt_NUM,
		};

		// player ID enum
		enum PlayerID
		{
			PlayerID_1,
			PlayerID_2,
			PlayerID_3,
			PlayerID_4,
			PlayerID_All,
		};

		// constructor
		IInputManager();

		// initialise the manager
		bool Initialise();

		// destroy the manager
		bool Destroy();

		// set a mapping from an input to an action (one-to-one, calling more than once will override existing mapping)
		void SetActionMapping(int context, int inputEvent, int actionID);

		// add an input listener
		void AddListener(IInputListener * listener, PlayerID playerID = PlayerID_All);

		// update the input manager
		void Update(const float dt);

		// set the players input mapping context
		void SetPlayerContext(PlayerID playerID, int contextID);

		// acquire input device (when window gets focus)
		virtual void AcquireDevices() = 0;

		// unacquire input devices (when window loses focus)
		virtual void UnacquireDevices() = 0;

	protected:
		// initialise platform specific devices
		virtual void InitialiseDevices() = 0;

		// shutdown platform specific devices
		virtual void DestroyDevices() = 0;

		// poll platform specific devices
		virtual void PollDevices() = 0;

		// queue up a single input event
		void QueueEvent( const InputEvent & e );

		// dispatch all events to listeners
		void DispatchEvents();

	protected:
		// the list of listeners
		typedef gtl::List<IInputListener*> Listeners;
		Listeners m_listeners;

		// the event queue
		static const int MAX_INPUT_EVENTS = 256;
		InputEvent m_inputEvents[MAX_INPUT_EVENTS];
		int m_numEvents;

		// events mapped to actions
		static const int MAX_PLAYERS = 4;
		static const int MAX_CONTEXTS = 16;
		int m_mappedActions[MAX_CONTEXTS][Evt_NUM];
		int m_playerContexts[MAX_PLAYERS];
	};

	IInputManager * CreateInputManager();

}

#endif // inputmanager_h__